/**
 * Copyright (c) 2024 Hunan OpenValley Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "GPUImageTransformFilter.h"
#include "OpenGlUtils.h"

const std::string GPUImageTransformFilter::TRANSFORM_VERTEX_SHADER = R"(
    attribute vec4 position;
    attribute vec4 inputTextureCoordinate;

    uniform mat4 transformMatrix;
    uniform mat4 orthographicMatrix;

    varying vec2 textureCoordinate;

    void main()
    {
        gl_Position = transformMatrix * vec4(position.x, -position.y, position.z, 1.0) * orthographicMatrix;
        textureCoordinate = inputTextureCoordinate.xy;
    }
)";

GPUImageTransformFilter::GPUImageTransformFilter()
    : GPUImageFilter(TRANSFORM_VERTEX_SHADER, NO_FILTER_FRAGMENT_SHADER)
{
    orthographicMatrix = new float[16];
    transform3D = new float[16];
    OpenGlUtils::orthoMatrix(orthographicMatrix, 0, -1.0f, 1.0f, -1.0f, 1.0f,
                             -1.0f, 1.0f);
    OpenGlUtils::setIdentityMatrix(transform3D, 0);
}

GPUImageTransformFilter::~GPUImageTransformFilter()
{
    delete[] orthographicMatrix;
    delete[] transform3D;
    orthographicMatrix = nullptr;
    transform3D = nullptr;
}

void GPUImageTransformFilter::onInit()
{
    GPUImageFilter::onInit();
    transformMatrixUniform =
        glGetUniformLocation(getProgram(), "transformMatrix");
    orthographicMatrixUniform =
        glGetUniformLocation(getProgram(), "orthographicMatrix");
}

void GPUImageTransformFilter::onInitialized()
{
    GPUImageFilter::onInitialized();
    setUniformMatrix4f(transformMatrixUniform, transform3D);
    setUniformMatrix4f(orthographicMatrixUniform, orthographicMatrix);
}

void GPUImageTransformFilter::onOutputSizeChanged(int width, int height)
{
    if (width == 0 || height == 0) {
        return;
    }
    GPUImageFilter::onOutputSizeChanged(width, height);
    if (!ignoreAspectRatio) {
        OpenGlUtils::orthoMatrix(orthographicMatrix, 0, -1.0f, 1.0f,
                                 -1.0f * height / width, 1.0f * height / width,
                                 -1.0f, 1.0f);
        setUniformMatrix4f(orthographicMatrixUniform, orthographicMatrix);
    }
}

void GPUImageTransformFilter::onDraw(int textureId, float *cubeBuffer,
                                     float *textureBuffer)
{
    float *vertBuffer = cubeBuffer;
    if (!ignoreAspectRatio) {
        float adjustedVertices[8];
        memcpy(adjustedVertices, cubeBuffer, sizeof(adjustedVertices));
        float normalizedHeight = (float)getOutputHeight() / (float)getOutputWidth();
        adjustedVertices[Vertices::LEFT_BOTTOM_Y] *= normalizedHeight;
        adjustedVertices[Vertices::RIGHT_BOTTOM_Y] *= normalizedHeight;
        adjustedVertices[Vertices::LEFT_TOP_Y] *= normalizedHeight;
        adjustedVertices[Vertices::RIGHT_TOP_Y] *= normalizedHeight;
        vertBuffer = new float[8];
        memcpy(vertBuffer, adjustedVertices, sizeof(adjustedVertices));
    }
    GPUImageFilter::onDraw(textureId, vertBuffer, textureBuffer);
    if (vertBuffer != cubeBuffer) {
        delete[] vertBuffer;
    }
}

void GPUImageTransformFilter::setTransform3D(float *transform3DArr)
{
    transform3D = transform3DArr;
    setUniformMatrix4f(transformMatrixUniform, transform3D);
}

float *GPUImageTransformFilter::getTransform3D() { return transform3D; }

void GPUImageTransformFilter::setIgnoreAspectRatio(bool ignoreAspectRatioVal)
{
    ignoreAspectRatio = ignoreAspectRatioVal;
    if (ignoreAspectRatio) {
        OpenGlUtils::orthoMatrix(orthographicMatrix, 0, -1.0f, 1.0f, -1.0f, 1.0f,
                                 -1.0f, 1.0f);
        setUniformMatrix4f(orthographicMatrixUniform, orthographicMatrix);
    } else {
        onOutputSizeChanged(getOutputWidth(), getOutputHeight());
    }
}

bool GPUImageTransformFilter::getIgnoreAspectRatio()
{
    return ignoreAspectRatio;
}

void GPUImageTransformFilter::setAnchorTopLeft(bool anchorTopLeftVal)
{
    anchorTopLeft = anchorTopLeftVal;
    setIgnoreAspectRatio(ignoreAspectRatio);
}

bool GPUImageTransformFilter::getAnchorTopLeft() { return anchorTopLeft; }